package org.study.shell;

import com.jcraft.jsch.*;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 在Linux上执行Shell命令,执行完成记得关闭
 */
public class ShellRun implements AutoCloseable{

    private Session session;

    /**
     * 通过Shell链接远程，初始化，默认端口22
     *
     * @param username 用户名
     * @param password 密码
     * @param host     远程地址
     * @throws Exception 异常信息
     */
    public ShellRun(String username, String password, String host) throws Exception {
        this(username, password, host, 22);
    }

    /**
     * 通过Shell链接远程，初始化
     *
     * @param username 用户名
     * @param password 密码
     * @param host     远程host
     * @param port     端口号
     * @throws Exception 异常信息
     */
    public ShellRun(String username, String password, String host, int port) throws Exception {
        this.initSession(username, password, host, port);
    }

    public Session getSession() {
        return this.session;
    }

    /**
     * 通过Shell链接远程，初始化
     *
     * @param username 用户名
     * @param password 密码
     * @param host     远程host
     * @param port     端口号
     * @throws Exception 异常信息
     */
    private void initSession(String username, String password, String host, int port) throws Exception {
        JSch jSch = new JSch();
        this.session = jSch.getSession(username, host, port);
        session.setPassword(password);
        session.setConfig("StrictHostKeyChecking", "no");
        session.connect();
    }

    /**
     * 文件上传，基于本地文件地址和远程文件地址
     *
     * @param local  本地文件地址
     * @param remote 远程文件地址
     * @throws Exception 异常信息
     */
    public void uploadBySftp(String local, String remote) throws Exception {
        ChannelSftp channel = (ChannelSftp) this.session.openChannel("sftp");
        channel.connect();
        channel.put(local, remote);
        channel.disconnect();
    }

    /**
     * 文件上传，本地基于流，远程地址
     *
     * @param is     输入流
     * @param remote 远程地址
     * @throws Exception 异常信息
     */
    public void uploadBySftp(InputStream is, String remote) throws Exception {
        ChannelSftp channel = (ChannelSftp) this.session.openChannel("sftp");
        channel.connect();
        channel.put(is, remote);
        channel.disconnect();
    }


    /**
     * 远程文件下载到指定目录
     *
     * @param remote  远程文件
     * @param local   本地目录
     * @param monitor 进度
     * @throws Exception 异常信息
     */
    public void downloadBySftp(String remote, String local, SftpProgressMonitor monitor) throws Exception {
        ChannelSftp channel = (ChannelSftp) this.session.openChannel("sftp");
        channel.connect();
        channel.get(remote, local, monitor);

        channel.disconnect();
    }

    /**
     * 文件下载到流
     */
    public String downloadBySftp(String remote, OutputStream out, SftpProgressMonitor monitor) throws Exception {
        ChannelSftp channel = (ChannelSftp) this.session.openChannel("sftp");
        //获取命令执行信息
        InputStream in = channel.getInputStream();
        //获取命令执行异常信息
        InputStream err = channel.getExtInputStream();
        channel.connect();
        channel.get(remote, out, monitor);
        String info = shellRunInfo(in, err, channel);
        channel.disconnect();
        return info;
    }

    /**
     * 执行shell 命令
     *
     * @param command shell命令
     * @return 返回执行输出信息
     * @throws JSchException shell异常
     * @throws IOException   输出输出异常
     */
    public String executeCommand(String command) throws JSchException, IOException {
        //获取执行
        ChannelExec channel = (ChannelExec) this.session.openChannel("exec");
        //设置执行命令
        channel.setCommand(command);
        //获取命令执行信息
        InputStream in = channel.getInputStream();
        //可以采用当前方式时候获取输出信息
        //channel.setInputStream();
        //获取命令执行异常信息
        InputStream err = channel.getErrStream();
        //开始连接，执行命令
        channel.connect();
//        channel.disconnect();
        //执行信息拼接，需要中途输出
        String info = this.shellRunInfo(in, err, channel);
        channel.disconnect();
        return info;
    }

    private String shellRunInfo(InputStream in, InputStream err, Channel channel) throws IOException {
        StringBuilder output = new StringBuilder();
        byte[] buffer = new byte[1024];
        while (true) {
            while (in.available() > 0) {
                int i = in.read(buffer, 0, 1024);
                output.append(new String(buffer, 0, i));
                System.out.print(new String(buffer, 0, i));
            }
            if (channel.isClosed()) {
                System.out.println("Exit status: " + channel.getExitStatus());
                break;
            }
            try {
                Thread.sleep(500);
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        // 处理错误流
        while (err.available() > 0) {
            int i = err.read(buffer, 0, 1024);
            output.append("[ERROR] ").append(new String(buffer, 0, i));
        }
        return output.toString();
    }

    /**
     * 关闭连接
     */
    public void close() {
        if (this.session != null && this.session.isConnected()) {
            this.session.disconnect();
        }
    }

}
